-- MonsterDisplay
-- Created by chengb Jan/14/2015
-- 负责怪物外观显示

require "game/ui/module/monster/MonsterAnimation"

-- MonsterDisplay继承自Node
MonsterDisplay = class("MonsterDisplay", function()
    return cc.Node:create();
end);

function MonsterDisplay.create(classId, para)
    return MonsterDisplay.new(classId, para);
end

-- 构造函数
function MonsterDisplay:ctor(classId, para)
    -- 记录怪物编号
    self.classId = classId;

    if para then
        self.skillId = para["skillId"];
        self.isDebug = para["isDebug"] or false;
    end

    -- 创建普通动画
    local animName = MonsterM.query(classId, "normal_anima");
    local animation = MonsterAnimation.create("animation/" .. animName .. ".csb");
    animation:setName("normal_anima");
    self.normalAnimation = animation;
    self:addChild(animation);

    -- 死亡动画
    local dieAnim =  MonsterM.query(self.classId, "die_anima");
    local dieAnimation = MonsterAnimation.create("animation/" .. dieAnim .. ".csb");
    self.dieAnimation = dieAnimation;
    self:addChild(dieAnimation);
    dieAnimation:setVisible(false);

    -- 播放初始动画
    self:playInitAnim();

    self:registerScriptHandler(function(ev)
        if ev == "exitTransitionStart" then
            if self.normalAnimation then
                if self.normalAnimation.action then
                    self.normalAnimation.action:clearFrameEventCallFunc();
                end

                self.normalAnimation:destroy();
            end

            if self.dieAnimation then
                if self.dieAnimation.action then
                    self.dieAnimation.action:clearFrameEventCallFunc();
                end
                self.dieAnimation:destroy();
            end
        end
    end);
end

-- 切换怪物动作
function MonsterDisplay:switchAction(actionType, extra)
    -- 待机
    if MONSTER_ACTION_IDLE == actionType then
        self:gotoIdle();
    -- 受创
    elseif MONSTER_ACTION_DAMAGED == actionType then
        self:gotoDamaged(extra);
    -- 反击
    elseif MONSTER_ACTION_STRIKE_BACK == actionType then
        self:gotoStrikeBack();
    -- 死亡
    elseif MONSTER_ACTION_DIE == actionType then
        self:gotoDie();
    -- 攻击
    elseif MONSTER_ACTION_ATTACK == actionType then
        self:gotoAttack(extra);
    -- 出场
    elseif MONSTER_ACTION_APPEAR == actionType then
        self:gotoAppear();
    -- 施法
    elseif MONSTER_ACTION_CAST == actionType then
        self:gotoCast();
    -- 自定义
    elseif MONSTER_ACTION_USER_DEFINED == actionType then
        self:gotoUserDefined(extra);
    -- 交替伴生动作
    elseif MONSTER_ACTION_ACCOMPANY == actionType then
        self:gotoAccompany();
    else
        -- 暂不支持
        cclog("### 未知的怪物动作类型：" .. tostring(actionType));
    end
end

-- 播放出场动作
function MonsterDisplay:gotoAppear()
    if self.normalAnimation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = MonsterM.query(self.classId, "appear_frame");
    local speed = MonsterM.query(self.classId, "appear_speed");

    if speed == 0 then
        return;
    end

    -- 尝试锁屏
    local lockScreen = (frameRange[2] - frameRange[1]) / 60 / speed;

    if type(lockScreen) == "number" and lockScreen > 0 and
        MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS then
        -- 因为怪物的受创动作延迟默认为0.5秒，所以这里锁屏最少也要0.5秒
        -- 额外加0.1秒是为了保证锁屏时间足够
        UIDungeonMgr:getCurLevel():lockScreen(math.max(lockScreen, 0.5) + 0.1);
    end

    -- 播放
    self.normalAnimation:play(MONSTER_ACTION_APPEAR, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent(frameRange[2]);
end

-- 播放待机动作
function MonsterDisplay:gotoIdle()
    if self.normalAnimation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = MonsterM.query(self.classId, "idle_frame");
    local speed = MonsterM.query(self.classId, "idle_speed") * 0.67;

    local function doPlay()
        if self.normalAnimation then
            self.normalAnimation:play(MONSTER_ACTION_IDLE, false, frameRange[1], frameRange[2], speed);
            if self.normalAnimation:getActionType() == MONSTER_ACTION_IDLE then
                local duration = (frameRange[2] - frameRange[1]) * 0.016 / speed;
                local randomDelay = math.random(40, 80) / 10;
                performWithDelay(self, doPlay, duration + randomDelay);
            end
        end
    end

    -- 如果是BOSS保持连贯呼吸，否则为了效率做个随机延迟
    if MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS or self.isDebug == true then
        self.normalAnimation:play(MONSTER_ACTION_IDLE, true, frameRange[1], frameRange[2], speed);
    else
        -- 先到动作第一帧
        self.normalAnimation:play(MONSTER_ACTION_IDLE, true, frameRange[1], frameRange[1], speed);

        local randomDelay = math.random(10, 50) / 10;
        performWithDelay(self, doPlay, randomDelay);
    end

    -- 需要暂停
    if self.needPause then
        self.normalAnimation:pause();
    end

    -- 待机音效播放
    local audioInfo = MonsterM.query(self.classId, "idle_audio");
    if type(audioInfo) == "table" then
        for frameIndex, audioId in pairs(audioInfo) do
            local function playAudio()
                AudioM.playFx(audioId);
            end

            -- 标记不需要移除帧回调
            self.normalAnimation.retainCB = true;

            -- BOOS有前置动画，需要延迟播放
            if MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS then
                performWithDelay(self, function()
                    if self.normalAnimation then
                        self:registerFrameEvent(frameIndex, playAudio);
                    end
                end, 5);
            else
                self:registerFrameEvent(frameIndex, playAudio);
            end
        end
    end
end

-- 播放自定义动作
function MonsterDisplay:gotoUserDefined(extra)
    if self.normalAnimation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = extra["frame"];
    local speed = extra["speed"];
    self.normalAnimation:play(MONSTER_ACTION_USER_DEFINED, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    if extra["goOn"] then
        self:registerFrameEvent(frameRange[2]);
    end
end

-- 播放交替伴生动作
function MonsterDisplay:gotoAccompany()
    -- 保持待机状态就好
    self:gotoIdle();
end

-- 变红
function MonsterDisplay:setHighlightMode(node, flag)
    if not node then
        return;
    end

    local children = node:getChildren();
    local len = table.getn(children)
    for i = 1, len do
        local child = children[i];
        local sprite = tolua.cast(child,"cc.Sprite");
        if sprite then
            setHighLightMode(sprite, flag);
        end

        self:setHighlightMode(child, flag);
    end
end

-- 震动
function MonsterDisplay:shake()
    local offset = 20;
    local angle = 30;

    local moveRight = cc.MoveBy:create(0.05, cc.p(offset, 10));
    local rotateTo = cc.RotateTo:create(0.05, angle, angle);
    local delay = cc.DelayTime:create(0.1);
    local moveBack = cc.MoveBy:create(0.05, cc.p(-offset, -10));
    local rotateBack = cc.RotateTo:create(0.05, 0, 0);
    local callFunc = cc.CallFunc:create(function()
        self:setHighlightMode(self.normalAnimation, false);
    end);
    local action = cc.Sequence:create(cc.Spawn:create(moveRight, rotateTo), delay, cc.Spawn:create(moveBack, rotateBack), callFunc);

    self:runAction(action);
    self:setHighlightMode(self.normalAnimation, true);
end

-- 播放受创动作
function MonsterDisplay:gotoDamaged(extra)
    if self.normalAnimation == nil then
        return;
    end

    -- 如果当前正在播放攻击动作，则不播放受创
    local attackActions = { MONSTER_ACTION_ATTACK, MONSTER_ACTION_CAST };
    if table.indexOf(attackActions, self.normalAnimation.getActionType()) ~= -1 then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = MonsterM.query(self.classId, "damaged_frame");
    local speed = MonsterM.query(self.classId, "damaged_speed") * 2;

    if speed == 0 then
        -- 没有受创动作，改为震动效果
        -- 如果有时间静止,那么不左右晃动
        if not extra or not extra["isPause"] then
            self:shake();
        end
        return;
    end

    -- 尝试锁屏
    local lockScreen = (frameRange[2] - frameRange[1]) / 60;

    if type(lockScreen) == "number" and lockScreen > 0 and
        MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS then
        -- 因为怪物的受创动作延迟默认为0.5秒，这里锁屏最少也要0.3秒
        -- 使用0.3秒是为了在受创动作播放结束前玩家就可以继续点击，这样操作感比较好
        if UIDungeonMgr:getCurLevel() then
            UIDungeonMgr:getCurLevel():lockScreen(math.max(lockScreen, 0.3));
        end
    end

    -- 播放
    self.normalAnimation:play(MONSTER_ACTION_DAMAGED, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent(frameRange[2]);
end

-- 播放反击动作
function MonsterDisplay:gotoStrikeBack()
    if self.normalAnimation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = MonsterM.query(self.classId, "strike_back_frame");
    local speed = MonsterM.query(self.classId, "strike_back_speed");

    if speed == 0 then
        -- 没有反击动作， 改为受创效果
        self:gotoDamaged();
        return;
    end

    -- 尝试锁屏
    --[[local lockScreen = (frameRange[2] - frameRange[1]) / 60 / speed;

    if type(lockScreen) == "number" and lockScreen > 0 and
        MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS then
        UIDungeonMgr:getCurLevel():lockScreen(lockScreen);
    end]]

    -- 播放
    self.normalAnimation:play(MONSTER_ACTION_STRIKE_BACK, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent(frameRange[2]);
end

-- 播放攻击动作
function MonsterDisplay:gotoAttack(extra)
    if self.normalAnimation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = MonsterM.query(self.classId, "attack_frame");
    local speed = MonsterM.query(self.classId, "attack_speed");

    extra = extra or {};
    local skillId = extra.skillId;

    if type(skillId) == "number" and skillId > 0 then
        local monsterDisplay = MonsterDisplayM.getMonsterSkillDisplay(self.classId, skillId);

        if monsterDisplay and #table.keys(monsterDisplay) > 0 then
            frameRange = monsterDisplay.attack_frame;
            speed = monsterDisplay.attack_speed;
        end

        -- 尝试锁屏
        local lockScreen = (frameRange[2] - frameRange[1]) / 60 / speed;

        if type(lockScreen) == "number" and lockScreen > 0 and
            MonsterM.query(self.classId, "monster_type") == MONSTER_TYPE_BOSS then
            UIDungeonMgr:getCurLevel():lockScreen(lockScreen);
        end
    end

    if speed == 0 then
        -- 没有攻击动作，改成震动效果
        -- self:shack();
        return;
    end

    -- 攻击动作音效
    local audioId = MonsterM.query(self.classId, "attack_audio");
    if type(audioId) == "string" and audioId ~= "" then
        AudioM.playFx(audioId);
    end

    -- 播放
    self.normalAnimation:play(MONSTER_ACTION_ATTACK, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent(frameRange[2]);
end

-- 播放施法动作
function MonsterDisplay:gotoCast()
    -- TODO 暂时不支持
end

-- 播放死亡动作
function MonsterDisplay:gotoDie()
    -- 显示死亡动画
    self.dieAnimation:setVisible(true);

    -- 获取帧范围和播放速度
    local frameRange = MonsterM.query(self.classId, "die_frame");
    local speed = MonsterM.query(self.classId, "die_speed");

    -- 播放
    self.dieAnimation:play(MONSTER_ACTION_DIE, false, frameRange[1], frameRange[2], speed);
end

-- 注册帧回调函数
-- @param endIndex   结束帧
function MonsterDisplay:registerFrameEvent(endIndex, callBack)
    -- 移除帧回调处理函数
    self.normalAnimation.action:clearFrameEventCallFunc();

    -- 帧回调处理函数
    local function onFrameEvent(frame)
        if nil == frame or nil == self.normalAnimation then
            return;
        end

        -- 使用pilor帧，确保每帧都会有回调过来
        if frame.getEvent and frame:getEvent() == "pilor" then
            -- 帧索引的下标是从0开始的，而配置的下标是从1开始的，故这里要加1
            local frameIndex = frame:getFrameIndex() + 1;
            -- 尚未到达结束帧，不处理
            if frameIndex ~= endIndex - 1 then
                return;
            end

            -- 移除帧回调处理函数
            if not self.normalAnimation.retainCB then
                self.normalAnimation.action:clearFrameEventCallFunc();
            end

            local function func()
                -- 委托回调
                if callBack then
                    callBack();
                else
                    -- 默认切换至待机状态
                    self:gotoIdle();
                end
            end

            performWithDelay(self, func, 0.1);
        end
    end

    -- 设置帧回调处理函数
    self.normalAnimation.action:setFrameEventCallFunc(onFrameEvent);
end

-- 移除普通动画
function MonsterDisplay:removeNormalAnimation()
    if self.normalAnimation == nil then
        return;
    end

    self.normalAnimation:pause();
    self.normalAnimation:destroy();
    self.normalAnimation = nil;
end

-- 暂停所有动画
function MonsterDisplay:pauseActions()
    if self.normalAnimation == nil then
        return;
    end
    -- 设置暂停
    self:setNeedPause(true);
    self:gotoIdle();
end

-- 设置是否需要暂停
function MonsterDisplay:setNeedPause(needPause)
    --self.needPause = needPause;
end

-- 获取播放速度
function MonsterDisplay:getSpeed(speed)
    return self.normalAnimation.action:getTimeSpeed();
end

-- 设置播放速度
function MonsterDisplay:setSpeed(speed)
    self.normalAnimation.action:setTimeSpeed(speed);
end

-- 播放初始动画
function MonsterDisplay:playInitAnim()
    local startFrame = MonsterM.query(self.classId, "dbase")["start_frame"];
    if startFrame and type(startFrame) == "table" then
        local skillId = startFrame["skill_id"];
        local frame = startFrame["frame"];
        if self.skillId == skillId then
            self.normalAnimation:play(MONSTER_ACTION_IDLE, true, frame[1], frame[2], 0.6);
            self:registerFrameEvent(frame[2]);
        else
            self:gotoIdle();
        end
    else
        -- 默认播放待机动作
        self:gotoIdle();
    end
end
